#include <pd_gpio.h>
#include <hd_led.h>
#include <hd_key.h>
#include <hd_oled.h>
#include <delay.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_oled_protocol_spi.h>
#include <pd_i2c_hd.h>
#include <pd_i2c_sim.h>
#include <pd_spi_io.h>
#include <stddef.h>
#include <pd_spi_hd.h>
#include <pd_spi_sim.h>
#include <hd_flash.h>
#include "main.h"

#define OLED_SPI            1   // Select a protocol in I2C or SPI
#define OLED_SPI_HARDWARE   1   // Select hardware or software simulate

static OLED *oled;

void oled_instance();

extern void SystemClock_Config(void);

int main(void) {
    SystemClock_Config();

    GPIO *ledGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_2);
    LED *led = LEDBuilder.build(ledGPIO);
    led->init(led);

    GPIO *keyGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_0);
    Key *key = KeyBuilder.build(keyGPIO);
    key->init(key);

    oled_instance();

    oled->full(oled);
    oled->refresh(oled);
    delay_ms(1000);

    oled->clear(oled);
    oled->refresh(oled);
    delay_ms(1000);

    oled->showBinNum(oled, 0, 0, 0xCFE, 12);
    oled->showHexNum(oled, 104, 0, 0xCFE, 3);

    oled->showHexNum(oled, 0, 16, 0xA1B2D3, 6);

    oled->showSignedNum(oled, 0, 32, 123, 3);
    oled->showSignedNum(oled, 40, 32, -123, 3);
    oled->showFloatNum(oled, 80, 32, (float) 1.2, 3);

    oled->showString(oled, 0, 48, "ASCII:");
    oled->printf(oled, 64, 48, "CODE:");
    oled->refresh(oled);

    uint8_t keyNum;
    uint8_t c = ' ';
    while (1) {
        keyNum = key->getKeyNum(key);
        if (keyNum != 1) {
            led->turn(led);

            if (c++ > '~') c = ' ';

            oled->showChar(oled, 48, 48, c);
            oled->showNum(oled, 104, 48, c - ' ', 3);

            oled->mode = OLED_MODE_NEGATIVE;
            oled->size = OLED_SIZE_24;
            oled->showChar(oled, 116, 16, c);

            oled->mode = OLED_MODE_POSITIVE;
            oled->size = OLED_SIZE_12;
            oled->showNum(oled, 80, 16, c - ' ', 3);
            oled->showChar(oled, 104, 16, c);

            oled->size = OLED_SIZE_16;
            oled->refresh(oled);

            delay_ms(1000);
        }
    }

}

void oled_instance() {
#ifdef OLED_SPI
    GPIO *sckGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_5); // Clock, SPIx_SCK
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_7); // MOSI,  SPIx_MOSI
    GPIO *resGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_3); // Reset, General IO, the chip reset pin is better
    GPIO *dcGPIO = GPIOBuilder.build(GPIOA, LL_GPIO_PIN_1);  // Data or Command, General IO
    GPIO *csGPIO[] = {
            GPIOBuilder.build(GPIOA, LL_GPIO_PIN_2)      // Chip Select(Slave Select), General IO
    };
    SPIGPIO *spiGPIO = SPIGPIOBuilder.build(csGPIO, 1, sckGPIO, sdaGPIO, NULL);

#ifdef OLED_SPI_SIMULATE
    SPISimulate *spiObj = SPISimulateBuilder.build(spiGPIO);
#elif OLED_SPI_HARDWARE
    SPIHardware *spiObj = SPIHardwareBuilder.build(SPI1, spiGPIO);
#endif
    SPI *spi1 = (SPI *) spiObj;
    spi1->init(spi1);
    OLEDProtocolSPI *oledProtocolObj = OLEDProtocolSPIBuilder.build(spi1, resGPIO, dcGPIO, csGPIO[0]);

#elif OLED_I2C
    GPIO *sckGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_10);  // I2Cx_SCL
    GPIO *sdaGPIO = GPIOBuilder.build(GPIOB, LL_GPIO_PIN_11);  // I2Cx_SDA
#ifdef OLED_I2C_SIMULATE
    I2CSimulate *i2CObj = I2CSimulateBuilder.build(sckGPIO, sdaGPIO);
#elif OLED_I2C_HARDWARE
    I2CHardware *i2CObj = I2CHardwareBuilder.build(I2C2, sckGPIO, sdaGPIO);
#endif
    I2C *i2c = (I2C *) i2CObj;
    i2c->init(i2c);
    OLEDProtocolI2C *oledProtocolObj = OLEDProtocolI2CBuilder.build(i2c);
#endif

    StorageFlash *storageFlash = StorageFlashBuilder.build();
    OLEDFont *oledFont = OLEDFontBuilder.build((Storage *) storageFlash, ENCODING_UTF8);
    oled = OLEDBuilder.build((OLEDProtocol *) oledProtocolObj, oledFont);
    oled->init(oled);
}
